package com.huawei.crm.util;


import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Function;

/**
 * @author luohua
 * @date 2021年12月10日15:41:38
 * @desc 锁工具(适配单体服务, 后续分布式要抽离到redis 、 zk等)
 */
public class ConcurrentHelpers {

    private static final ConcurrentMap<Long, Deque<LogicLock>> LOCKS = new ConcurrentHashMap<>();

    public static void execute(LogicLock logicLock, String lockerString, Runnable runnable) {
        validateLockerString(logicLock, lockerString);

        Deque<LogicLock> currentLogicLocks = getCurrentLockStack();
        int currentTopLockLevel = getTopLockLevel(currentLogicLocks);
        if (logicLock.getLevel() >= currentTopLockLevel) {
            throw new GeneralException(String.format("逻辑锁不可越前: 当前 %d, 新进 %d", currentTopLockLevel, logicLock.getLevel()));
        }
        currentLogicLocks.push(logicLock);
        synchronized (lockerString.intern()) {
            try {
                runnable.run();
            } finally {
                currentLogicLocks.pop();
            }
        }
    }

    public static <T, R> R execute(LogicLock logicLock, String lockerString, T parameter, Function<T, R> func) {
        validateLockerString(logicLock, lockerString);

        Deque<LogicLock> currentLogicLocks = getCurrentLockStack();
        int currentTopLockLevel = getTopLockLevel(currentLogicLocks);
        if (logicLock.getLevel() >= currentTopLockLevel) {
            throw new GeneralException(String.format("逻辑锁不可越前: 当前 %d, 新进 %d", currentTopLockLevel, logicLock.getLevel()));
        }
        currentLogicLocks.push(logicLock);
        synchronized (lockerString.intern()) {
            try {
                return func.apply(parameter);
            } finally {
                currentLogicLocks.pop();
            }
        }
    }

    public static String buildLockerString(String... args) {
        return String.join(":", args);
    }

    private static void validateLockerString(LogicLock logicLock, String lockerString) {
        if (!lockerString.startsWith(logicLock.getPrefix())) {
            throw new GeneralException(String.format("%s 锁错误前缀", lockerString));
        }
    }

    private static Deque<LogicLock> getCurrentLockStack() {
        long currentThreadId = getCurrentThreadId();
        LOCKS.putIfAbsent(currentThreadId, new ArrayDeque<>());
        return LOCKS.get(currentThreadId);
    }

    private static long getCurrentThreadId() {
        return Thread.currentThread().getId();
    }

    private static int getTopLockLevel(Deque<LogicLock> logicLocks) {
        if (logicLocks.isEmpty()) {
            return Integer.MAX_VALUE;
        }
        return logicLocks.peek().getLevel();
    }
}
